package com.aitao.util;

import com.aitao.model.BeanProperties;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Company 11bee
 * @Author : tao.ai
 * @Create : 2021/09/18 15:28
 * @Description : 对象属性拷贝工具类
 * @see com.aitao.test.TestBeanUtils 测试类
 */
public class BeanUtils {
    //缓存被解析过的对象信息
    private static final Map<String, Map<String, BeanProperties>> BEAN_PROPERTIES_CACHE = new ConcurrentHashMap<>();
    //缓存被解析过的对象信息(忽略大小写)
    private static final Map<String, Map<String, BeanProperties>> IGNORE_CASE_BEAN_PROPERTIES_CACHE = new ConcurrentHashMap<>();

    /**
     * 校验className类中的filedName字段是否是被public修饰
     *
     * @param className 类型名
     * @param filedName 字段名
     * @return true or false
     */
    public static boolean isPublicFiled(String className, String filedName) {
        try {
            return Modifier.isPublic(Class.forName(className).getDeclaredField(filedName).getModifiers());
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 校验className类中的filedName字段是否是被protected修饰
     *
     * @param className 类型名
     * @param filedName 字段名
     * @return true or false
     */
    public static boolean isProtectedField(String className, String filedName) {
        try {
            return Modifier.isProtected(Class.forName(className).getDeclaredField(filedName).getModifiers());
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 校验className类中的filedName字段是否是被private修饰
     *
     * @param className 类型名
     * @param filedName 字段名
     * @return true or false
     */
    public static boolean isPrivateField(String className, String filedName) {
        try {
            return Modifier.isPrivate(Class.forName(className).getDeclaredField(filedName).getModifiers());
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 拷贝同名属性
     *
     * @param srcBean  源对象
     * @param destBean 目标对象
     */
    public static void copyProperties(Object srcBean, Object destBean) {
        copyProperties(srcBean, destBean, false);
    }

    /**
     * 拷贝同名属性
     *
     * @param srcBean    源对象
     * @param destBean   目标对象
     * @param ignoreCase 是否忽略大小写
     */
    public static void copyProperties(Object srcBean, Object destBean, boolean ignoreCase) {
        try {
            //解析bean对象
            resolveBean(srcBean.getClass());
            resolveBean(destBean.getClass());
            Map<String, BeanProperties> srcMap, destMap;
            if (ignoreCase) {
                srcMap = BEAN_PROPERTIES_CACHE.get(srcBean.getClass().getName());
                destMap = BEAN_PROPERTIES_CACHE.get(destBean.getClass().getName());
            } else {
                srcMap = IGNORE_CASE_BEAN_PROPERTIES_CACHE.get(srcBean.getClass().getName());
                destMap = IGNORE_CASE_BEAN_PROPERTIES_CACHE.get(destBean.getClass().getName());
            }
            //求两个map交集
            Map<String, BeanProperties> newMap = Collector.intersect(srcMap, destMap);
            for (String key : newMap.keySet()) {
                BeanProperties src = srcMap.get(key);
                BeanProperties dest = srcMap.get(key);
                // 调用 destBean 对象中的setter方法设置值
                // 调用 srcBean 中的getter方法获取值
                dest.getSetMethod().invoke(destBean, src.getGetMethod().invoke(srcBean));
            }
        } catch (IntrospectionException | InvocationTargetException | IllegalAccessException e) {
            throw new RuntimeException("对象拷贝失败", e);
        }
    }

    /**
     * 解析bean对象,并获取对应的信息
     *
     * @param clazz Bean实例的类对象
     */
    private static void resolveBean(Class<?> clazz) throws IntrospectionException {
        String className = clazz.getName();
        Map<String, BeanProperties> currentBean = BEAN_PROPERTIES_CACHE.get(className);
        if (currentBean == null || currentBean.isEmpty()) {
            BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            if (propertyDescriptors != null && propertyDescriptors.length > 0) {
                Map<String, BeanProperties> beanProperties = new ConcurrentHashMap<>();
                Map<String, BeanProperties> ignoreCaseBeanProperties = new ConcurrentHashMap<>();
                Arrays.stream(propertyDescriptors).filter(pd -> !pd.getName().equals("class")).forEach(pd -> {
                    String fieldName = pd.getName();
                    BeanProperties info = new BeanProperties(fieldName, pd.getPropertyType(), pd.getReadMethod(), pd.getWriteMethod(), isPublicFiled(className, fieldName), isProtectedField(className, fieldName), isPrivateField(className, fieldName));
                    beanProperties.put(fieldName, info);
                    ignoreCaseBeanProperties.put(fieldName.toLowerCase(), info);
                });
                BEAN_PROPERTIES_CACHE.put(className, beanProperties);
                IGNORE_CASE_BEAN_PROPERTIES_CACHE.put(className, ignoreCaseBeanProperties);
            }
        }
    }
}
